package com.weido.nettyServer.nettyMvc;


import com.weido.nettyServer.core.*;
import com.weido.nettyServer.annotation.Path;
import com.weido.nettyServer.annotation.RespJson;
import com.weido.nettyServer.utils.JsonUtil;
import com.weido.nettyServer.utils.MethodUtil;
import com.weido.nettyServer.utils.ScanClassUtil;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.*;
import java.util.regex.Pattern;

/**
 * 类似springmvc的将请求全部纳入控制范围的servlet
 *
 * @author rongdi
 * @date 2017年9月20日 下午1:58:26
 */
@Component
public class NettyDispatcherServlet extends MiNiServlet {

    private static final Logger logger = LoggerFactory.getLogger(NettyDispatcherServlet.class);

    //处理Ccontroller
    @Value("${netty.mvc.package}")
    private String packageName;
    private static final long serialVersionUID = 1L;

    private WebContext webContext;
    /*  映射器处理器  */
    private static MappingHandler mappingHandler;
    ;
    /*  处理器*/
    private static HandlerAdaptor handler;

    public void init(ClassPathXmlApplicationContext context) {
        mappingHandler = new MappingHandler(packageName);
        handler = new HandlerAdaptor();
        FilterAsist.filterMap = context.getBeansOfType(Filter.class);
        logger.info("Servlet is init!");
    }

    @Override
    public void doGet(HttpRequest request, HttpResponse response) {
        webContext = new WebContext(request);
        try {
            Map<String, Object> params = request.getParameterMap();
            String url = request.getUrl().split("\\?")[0];
            //当请求根路径是，请求转发到首页
            if ("/".equals(url)) {
                response.write("欢迎使用nettyMvc");
                return;
            }
            if ("/favicon.ico".equals(url)) {
                return;
            }
            Method m = null;
            if (mappingHandler.hasMethod(url)) {
                m = mappingHandler.getMethod(url);
            } else {
                //这个过程，其实可以优化，如果存在通配符匹配，不用每次都循环匹配，可以缓存起来，第二次直接用，这里暂时忽略优化问题
                Set<String> urls = mappingHandler.getMethodMap().keySet();
                for (String murl : urls) {
                    String reg = "^" + murl.replace("*", ".*?") + "$";
                    if (Pattern.matches(reg, url)) {
                        m = mappingHandler.getMethod(murl);
                        break;
                    }
                }
            }
            if (m == null) {
                response.writeError("404", HttpResponseStatus.NOT_FOUND);
                return;
            }
            /**
             * 这里需要获取参数名，jdk1.8之后可以直接直接反射获取，条件比较恶心，需要开启开关
             * 如下直接使用javassist字节码工具类实现，也可以用asm等其他工具
             */
            Type[] paramTypes = MethodUtil.getMethodParam(m);
            List<Object> paramValues = new ArrayList<Object>();
            if (paramTypes.length > 0 && paramTypes[0].equals(webContext.getClass())) {
                paramValues.add(webContext);
            } else {
                Object[] paramNames = MethodUtil.getAllParamaterName(m);
                for (Object paramName : paramNames) {
                    if (params.get(paramName) == null) {
                        paramValues.add(null);
                    } else {
                        paramValues.add(params.get(paramName));
                    }
                }
            }
            /**
             * 调用方法所在类的默认构造方法，生成执行方法的对象（springmvc里的这个对象是单例的，这里为了省事，每次都new一个出来），
             * 然后执行方法，返回结果
             */
            Class<?> cla = m.getDeclaringClass();
            Object result = m.invoke(cla.newInstance(), paramValues.toArray());

            /**
             * 这里springmvc默认是请求转发到jsp
             * 为了方便这里直接根据修饰返回类型的注解，确定用哪种方式序列化，
             */
            if (handler.isRespJson(cla, m)) {
                result = JsonUtil.serialize(result);
                //输出json到浏览器
                if (result == null) {
                    response.write(null);
                }
                response.write(result.toString());
            } else if (result.toString().startsWith("redirect:")) { //重定向
                //去掉前缀就是重定向到的路径,实际上这里不严谨,应该加上一个项目的上下文路径
                //   response.sendRedirect(result.toString().substring(9)+".jsp");
            } else { //请求转发
                // request.getRequestDispatcher(result.toString()+".jsp").forward(request, response);
            }
        } catch (Exception e) {
            e.printStackTrace();
            response.write(e.getMessage());
        }
    }

    @Override
    public void doPost(HttpRequest request, HttpResponse response) {
        doGet(request, response);
    }

    public static void main(String[] args) {
        String url = "/test/a";
        String reg = "^/test/.*?$";
        System.out.println(Pattern.matches(reg, url));
        System.out.println(Long.class);

    }
}